package com.cgs.query.handler.render

import com.cgs.query.domain.*
import com.cgs.query.exception.QueryException
import com.cgs.query.executor.ExecutionContext
import com.cgs.query.handler.ParameterHandler
import com.cgs.query.util.EnumUtils
import mu.KotlinLogging
import java.sql.JDBCType

private val logger = KotlinLogging.logger { }

/**sql参数解析器：#{},${}*/
class MybatisParameterHandler : ParameterHandler {

    /**正则匹配*/
    private val pattern = "(#|\\\$)\\{\\w+(,\\w+){0,2}\\}".toPattern()

    private val types = mapOf("$" to Type.REPLAY, "#" to Type.PARAM)

    override fun resolve(executionObj: ExecutionObj, param: Map<String, Any?>): BoundSql {

        val statement = executionObj.statement

        val matcher = pattern.matcher(statement)

        val parameterMappings = arrayListOf<ParameterMapping>()

        val sql = StringBuilder()

        var start = 0

        while (matcher.find()) {

            var group = matcher.group()

            sql.append(statement.substring(start, matcher.start()))

            start = matcher.end()

            var type = types["" + group.first()]

            type?.resolveArgs(sql, group.substring(2, group.length - 1), param, parameterMappings)

        }

        if (start == 0)
            sql.append(statement)
        else
            sql.append(statement.substring(start, statement.length))

        return BoundSql(sql.toString(), parameterMappings).apply {
            logger.debug { "Sql解析：[$this]}" }
        }
    }
}

/**注入方式*/
enum class Type {
    /**${}*/
    REPLAY() {
        override fun resolveArgs(sb: StringBuilder, key: String, param: Map<String, Any?>, result: ArrayList<ParameterMapping>) {
            sb.append(param[key])
        }
    },
    /**#{}*/
    PARAM() {
        override fun resolveArgs(sb: StringBuilder, key: String, param: Map<String, Any?>, result: ArrayList<ParameterMapping>) {
            result.add(key.split(",").let {
                ParameterMapping(key, "").apply {
                    if (it.size == 3) {
                        mode = EnumUtils.valueOf(ParameterMode::class.java, it[1])
                        jdbcType = EnumUtils.valueOf(JDBCType::class.java, it[2])
                    }
                    if (mode == ParameterMode.IN || mode == ParameterMode.INOUT)
                        value = param[it[0]] ?: throw QueryException("参数:${it[0]}不能为Null")
                }
            })
            sb.append("?")
        }
    };

    abstract fun resolveArgs(sb: StringBuilder, key: String, param: Map<String, Any?>, result: ArrayList<ParameterMapping>)
}


